#include <lampe/StringUtils.h>

#ifndef NOT_USING_ICONV_LIB
#include <iconv.h>
#include <lampe/thread/IMutex.h>
#endif

#include <string.h>
#include <cstdarg>
#include <lampe/config.h>

#ifndef NOT_USING_MD5_LIB
#include "MD5.h"
#endif
#include "Log.h"

namespace lampe{
namespace str{

#ifndef NOT_USING_ICONV_LIB
/*
lampe::String conv_charset(lampe::String from,lampe::String to,lampe::String str)
// char *str, int *size
{
	MARKER;
	if (str=="")
		return str;
    char *out;
	MARKER;
        char *buf;
        size_t insize, outsize, bufsize, r;
        iconv_t cd; 
MARKER;
        cd = iconv_open(to.c_str(), from.c_str());
        if (cd == (iconv_t) - 1){
		MARKER;
			lampe::String errtext="";
			if (errno==EMFILE)
				errtext="EMFILE";
				
			if (errno==ENFILE)
				errtext="ENFILE";
				
			if (errno==ENOMEM)
				errtext="ENOMEM";
				
			if (errno==EINVAL)
				errtext="EINVAL";
			
            return lampe::String("ER1 ")+errtext;
		}
		MARKER;
        insize = str.length();//*size;
        outsize = bufsize = insize * 4;
		
        buf = new char[bufsize+1];
		char*ss=(char*)str.c_str();
        r = iconv(cd, &ss, &insize, &buf, &outsize);
        if (r < 0 || insize != 0) {
			MARKER;
			delete []buf;
            iconv_close(cd);
            return "ER2";
        }
		MARKER;
		buf[bufsize]=char(0);
		buf[outsize-1]=char(0);
		MARKER;
		lampe::String res=lampe::String(buf);
		MARKER;
		delete [] buf;
		MARKER;

        iconv_close(cd);
		
		MARKER;
    return res;
}
*/




	lampe::String conv_charset(lampe::String from,lampe::String to,lampe::String text){
		
		iconv_t cnv = iconv_open(to.c_str(),from.c_str());
		if (cnv==(iconv_t)-1){
			iconv_close(cnv);
			return "";
		}
		char*outbuf=new char[text.length()*2+1];
		if (outbuf==0){
			iconv_close(cnv);
			return "";
		}
		char*ip=(char*)text.c_str();
		char*op=outbuf;
		size_t icount=text.length();
		size_t ocount=text.length()*2;
		lampe::String res;
		if (iconv(cnv,&ip,&icount,&op,&ocount)!=(size_t)-1){
			outbuf[text.length()*2-ocount]='\0';
			res=outbuf;
		}else{
			res="";
		}

		delete[]outbuf;
		iconv_close(cnv);
		
		return res;
	}

#endif
	lampe::String str_replace(const lampe::String search, const lampe::String replace, lampe::String subject)
	{
		//printf("replace \"%s\" on \"%s\"\n",search.c_str(),replace.c_str());
		//printf("%s -> %s\n",search.c_str(),replace.c_str());
		lampe::String buffer;

		u32 sealeng = search.length();
		u32 strleng = subject.length();

		if (sealeng==0)
			return subject;//no change

		for(u32 i=0, j=0; i<strleng; j=0 )
		{
			while (i+j<strleng && j<sealeng && subject[i+j]==search[j])
				j++;
			if (j==sealeng)//found 'search'
			{
				buffer.append(replace);
				i+=sealeng;
			}
			else
			{
				buffer.append( &subject[i++], 1);
			}
		}
		subject = buffer;
		return subject;
	}

	lampe::s32 findFirstCharNotInList(lampe::String str,lampe::String c){
		for (u32 i=0; i<str.length(); ++i){
			bool t=false;
			for (u32 j=0; j<c.length(); ++j){
				if (str[i]==c[j]){
					t=true;
					break;
				}
			}
			if (!t)
				return i;
			if (t && i==str.length()-1)
				return i;
		}

		return -1;
	}

	lampe::s32 findLastCharNotInList(lampe::String str,lampe::String c){
		for (u32 i=str.length()-1; i!=0; --i){
			bool t=false;
			for (u32 j=0; j<c.length(); ++j){
				if (str[i]==c[j]){
					t=true;
					break;
				}
			}
			if (!t)
				return i;
			if (t && i==0)
				return i;
		}

		return -1;
	}

	lampe::String trim(String a){
		String whitespace=" \t\n\r";
				const s32 begin = findFirstCharNotInList(a,whitespace);
				if (begin == -1)
					return a;

				const s32 end = findLastCharNotInList(a,whitespace);
				//const s32 end=a.length();
				if (begin==end || begin>end)
					return "";
				return a.substr(begin, (end +1) - begin);
	}

	s32 find(lampe::String str,lampe::String find,u32 pos){
		return str.find(find,pos);
	}

	lampe::String toLowerCase(lampe::String str){
		for (u32 i=0;i<str.length();i++)
			str[i]=tolower(str[i]);
		return str;
	}

	lampe::String toUpperCase(lampe::String str){
		for (u32 i=0;i<str.length();i++)
			str[i]=toupper(str[i]);
		return str;
	}

	bool isNumber(char a){
		return (a=='0' || a=='1' || a=='2' || a=='3' || a=='4' || a=='5' || a=='6' || a=='7' || a=='8' || a=='9');
	}

	bool isNumber(lampe::String value){
		u32 i=0;
		while (i<value.length()){
			if (isNumber(value[i]) || (i==0 && value[i]=='-'))
				i++;
			else
				return false;
		}
		return true;
	}
#ifndef NOT_USING_MD5_LIB
	s32 makeID(lampe::String value){
		value=trim(value);
		if (isNumber(value)){
			return toI32(value);
			//sscanf(value.c_str(),"%d",&res);
			//return res;
		}
		return md5summ(value);
	}
#endif
	void split(lampe::String str,LAMPE_VECTOR_VENDOR<lampe::String>*list,char separator){
		u32 i=0;
		u32 p=0;
		list->clear();
		while (i<str.length()){
			if (str[i]==separator){
				list->push_back(str.substr(p,i-p));
				p=i;
			}
			i++;
		}

		if (p>0)
			list->push_back(str.substr(p+1,str.length()));
	}
	/*
	LAMPE_VECTOR_VENDOR<String> split(lampe::String str,char separator){
		LAMPE_VECTOR_VENDOR<lampe::String> list;
		u32 i=0;
		u32 p=0;
		list.clear();
		while (i<str.length()){
			if (str[i]==separator){
				list.push_back(str.substr(p,i-p));
				p=i;
			}
			i++;
		}

		if (p>0)
			list.push_back(str.substr(p+1,str.length()));
		return list;
	}
*/
	lampe::String fromU62(u32 value){
		c8 tmpbuf[255];
		snprintf(tmpbuf, 255, "%ul", value);
		return lampe::String(tmpbuf);
	}

	lampe::String fromU32(u32 value){
		c8 tmpbuf[255];
		snprintf(tmpbuf, 255, "%u", value);
		return lampe::String(tmpbuf);
	}

	lampe::String fromF32(f32 value){
		c8 tmpbuf[255];
		snprintf(tmpbuf, 255, "%0.6f", value);
		return lampe::String(tmpbuf);
	}

	lampe::String fromS32(s32 value){
		c8 tmpbuf[255];
		snprintf(tmpbuf, 255, "%d", value);
		return lampe::String(tmpbuf);
	}


	s32 toI32(lampe::String value){
		s32 v;
		sscanf(value.c_str(),"%d",&v);
		return v;
	}

	f32 toF32(lampe::String value){
		f32 v;
		sscanf(value.c_str(),"%f",&v);
		return v;
	}

	u32 toU32(lampe::String value){
		u32 v;
		sscanf(value.c_str(),"%u",&v);
		return v;
	}

	bool startWith(lampe::String str,lampe::String findStr){
		return str.find(findStr)==0;
	}

	bool endWith(lampe::String str,lampe::String find){
		if (find.length()<=0)
			return false;
		if (str.length()<find.length())
			return 0;
		if (str.substr(str.length()-find.length())==find)
			return true;
		return false;
	}

#define STATIC_STRING_BUFFER
	lampe::String format(const char*fmt,...){
		lampe::String retStr("");
		if (0 != fmt){
			va_list marker;
			va_start(marker, fmt);
			#ifdef WINDOWS_PLATFORM
			size_t len = _vscprintf(fmt, marker);
			#endif
            #ifdef POSIX_LIBLARY
			char c;
			size_t len=vsnprintf(&c, 1, fmt, marker);
			#endif
			#ifdef STATIC_STRING_BUFFER
			c8 buffer[256];
			#else

			c8*buffer=(c8*)malloc(len+1);
			

			#endif
			buffer[len]=char(0);
			int nWritten = vsprintf(&buffer[0],fmt,marker);
			if (nWritten > 0)
				retStr = &buffer[0];
			va_end(marker);
			lampe::String res;
			res.assign(&buffer[0],len);
			#ifndef STATIC_STRING_BUFFER
			free(buffer);
			#endif
			return res;
		}
		return "";
	}

	const char *memstr (const char *where, const char *what, int Number){
		if (!Number)
			return where;
		Number++;
		do{
			const char*we;
			const char*wa;
			int N=Number;
			for (we=where,wa=what;--N&&toupper(*we++)==toupper(*wa++););
			if (!N)
				return where;
		}while(*where++);
		return 0;
	}

	bool mask_match (const char *str, const char *mask){
		while(true){
			if (*mask=='*'){
				const char*scan=mask+1;
				int N=0;
				while(*++mask!='*'&&*mask!='?'&&*mask)
					++N;
				if(!*mask)
					++N;
				if(!(str=memstr(str,scan,N)))
					return false;
				if (!str[N-1])
					return true;
				str += N;
				continue;
			}
			if (*mask == '?'){
				if (!*str)
					return false;
				str++;
				mask++;
				continue;
			}
			if (toupper(*mask)!=toupper(*str))
				return false;
			if(!*mask)
				return true;
			str++;
			mask++;
		}
	}

}//namespace str
}//namespace lampe
